package com.navi.loaddata.loadService.impl.mesData.defectData;

import com.alibaba.fastjson.JSONObject;
import com.navi.loaddata.core.errorLog.ErrorCode;
import com.navi.loaddata.core.etlException.ETLException2;
import com.navi.loaddata.core.gpEntity.File.FileDefectPojo;
import com.navi.loaddata.core.gpEntity.MppLayoutPnlD;
import com.navi.loaddata.core.mysqlEntity.EtlConfDModel;
import com.navi.loaddata.core.util.JacksonUtil;
import com.navi.loaddata.core.util.LoggerUtil;
import com.navi.loaddata.loadService.exception.CoordTransformException;
import com.navi.loaddata.loadService.impl.mesData.LoadMESDataBean;
import com.navi.loaddata.loadService.impl.mesData.MppLayoutPnlDService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.math.BigDecimal;
import java.sql.SQLRecoverableException;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class LoadMESDefectService {
    private static final Logger logger = LoggerFactory.getLogger(LoadMESDefectService.class);
    @Autowired
    private AmqpTemplate rabbitTemplate;
    @Autowired
    private LoadMESDefectInfoService loadMESDefectInfoService;
    @Autowired
    private MppLayoutPnlDService mppLayoutPnlDService;

    @Autowired
    private LoadMESDataBean loadMESDataBean;

    @Autowired
    private JedisPool jedisPool;

    public static final int TOP_LEFT = 0;
    public static final int TOP_RIGHT = 1;
    public static final int LOWER_RIGHT = 2;
    public static final int LOWER_LEFT = 3;
    public static final int DATA_TO_X = 0;
    public static final int DATA_TO_Y = 1;

    public String[] loadMESDefectDataMain(EtlConfDModel etlConfD) throws Exception {
        String[] resMsg = loadMESDefect(etlConfD);
        logger.info("loadMESDefectDataMain finish");
        return resMsg;
    }

    private String[] loadMESDefect(EtlConfDModel etlConfD) throws Exception {
        String[] resMsg = new String[3];
        int okCnt = 0;
        int errCnt = 0;
        String errMsg = null;

        List<FileDefectPojo> defectInfoList = new ArrayList<>();

        try {
            defectInfoList = loadMESDefectInfoService.getMESDefectInfo(etlConfD);
        } catch (SQLRecoverableException e) {
            logger.error("报错信息：" + LoggerUtil.stackTraceToString(e));
            etlConfD.setRunFlg("N");
            loadMESDefectInfoService.updateEtlConfD(etlConfD);

            resMsg[0] = String.valueOf(okCnt);
            resMsg[1] = String.valueOf(errCnt);
            resMsg[2] = errMsg;

            return resMsg;
        }
        logger.info("MES Defect 共{}笔数据", defectInfoList.size());

        if (!defectInfoList.isEmpty()) {
            for (FileDefectPojo defectInfo : defectInfoList) {
                try {
                    logger.info("glassId={} opeId={} jgeCnt={}开始捞取数据。",
                            defectInfo.getGlassId(), defectInfo.getOpeId(), defectInfo.getJgeCnt());

                    //Rabbit MQ需要给ope_tbl_name的值（表名）
                    defectInfo.setOpe_tbl_name("wpp_cdefect_f_n");

                    String opeId = etlConfD.getOpe();
                    String glassId = defectInfo.getGlassId();
                    String evtTimestamp = defectInfo.getEvtTimestamp();

                    Map<String, List<MppLayoutPnlD>> MppLayoutPnlDsMap =new HashMap<>();
                    List<MppLayoutPnlD> layoutPnlDList = new ArrayList<>();
                    layoutPnlDList = mppLayoutPnlDService.getMppLayoutPnlDByPanelId(defectInfo.getPanelId());
                    MppLayoutPnlDsMap.put(defectInfo.getPanelId(),layoutPnlDList);

                    List<MppLayoutPnlD> layoutPnlDS=MppLayoutPnlDsMap.get(defectInfo.getPanelId());
                    int panelNo = Integer.parseInt(defectInfo.getPanelId().substring(defectInfo.getPanelId().length() - 2, defectInfo.getPanelId().length()));

                    //转换gate_no、data_no为横纵坐标
                    if (!(defectInfo.getDataNo().indexOf("*") > 0 || defectInfo.getGateNo().indexOf("*") > 0)) {
                        if ("NONE".equals(defectInfo.getGateNo()) && "NONE".equals(defectInfo.getDataNo())) {
                            defectInfo.setDefectXCoord("");
                            defectInfo.setDefectYCoord("");
                        } else if (("NONE".equals(defectInfo.getGateNo()) && !"NONE".equals(defectInfo.getDataNo())) || (!"NONE".equals(defectInfo.getGateNo()) && "NONE".equals(defectInfo.getDataNo()))) {
                            //线Defect时，DataNo或GateNo为NONE时，赋值为0
                            if ("NONE".equals(defectInfo.getDataNo())) {
                                defectInfo.setDataNo("0");
                            } else {
                                defectInfo.setGateNo("0");
                            }

                            logger.info("转线Defect坐标开始");
                            HashMap<String, Integer> coordinateList = this.small2Big2(defectInfo.getPanelId(), layoutPnlDS, Double.valueOf(defectInfo.getDataNo()), Double.valueOf(defectInfo.getGateNo()));
                            logger.info("转线Defect坐标结束");

                            if (coordinateList.size() > 0) {
                                Collection<Integer> valueList = coordinateList.values();
                                Object[] obj = valueList.toArray();

                                defectInfo.setDefectXCoord(String.valueOf(obj[0]));
                                defectInfo.setDefectYCoord(String.valueOf(obj[1]));
                            }

                        } else {
                            logger.info("转点Defect坐标开始");
                            Map<String, Integer> coordinateList = this.small2big(panelNo, layoutPnlDS, Double.valueOf(defectInfo.getDataNo()), Double.valueOf(defectInfo.getGateNo()));
                            logger.info("转点Defect坐标结束");

                            if (coordinateList.size() > 0) {
                                Collection<Integer> valueList = coordinateList.values();
                                Object[] obj = valueList.toArray();

                                defectInfo.setDefectXCoord(String.valueOf(obj[0]));
                                defectInfo.setDefectYCoord(String.valueOf(obj[1]));
                            }
                        }
                    }

                    //设定存redis的key为opeId+glassId+evtTimestamp
                    String defectKey = String.format("RPT_DEFECT:opeId:%s glassId:%s", opeId, glassId);

                    String value = "";
                    String newValue = "";

                    try (Jedis jedis = jedisPool.getResource()) {
                        value = jedis.get(defectKey);

                        if (value == null || value.length() == 0) {
                            newValue = defectInfo.getJgeCnt() + "_" + evtTimestamp;
                            defectInfo.setRealFlg("Y");
                        } else {
                            SimpleDateFormat dt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

                            //匹配evt_timestamp是否大于redis记录的时间，若大于，说明为正常跑数据，记录新的evt_timestamp且jge_cnt加1，redis记录evt_timestamp时间不变，若小于或等于，说明是数据重跑
                            if (dt.parse(evtTimestamp).compareTo(dt.parse(value.substring(value.indexOf("_") + 1))) > 0) {
                                newValue = defectInfo.getJgeCnt() + "_" + evtTimestamp;
                                //ope_id、glass_id相同，而evt_timestamp不同，说明做了重工，前一笔数据的real_flg需置为N
                                String oldEvtTimestamp = value.substring(value.indexOf("_")+1);
                                updateRealFlg(opeId.substring(1,opeId.length()), glassId, oldEvtTimestamp, value.substring(0, value.indexOf("_")));
                                defectInfo.setRealFlg("Y");
                            } else if (dt.parse(evtTimestamp).compareTo(dt.parse(value.substring(value.indexOf("_") + 1))) == 0) {
                                newValue = value;
                                defectInfo.setRealFlg("Y");
                            } else {
                                newValue = value;
                                defectInfo.setRealFlg("N");
                            }
                        }

                        //在redis里记录新的jge_cnt和evt_timestamp值
                        jedis.set(defectKey, newValue);
                        jedis.expire(defectKey, 15552000); // 设置过期时间为6个月
                    } catch (Exception e) {
                        logger.error(LoggerUtil.stackTraceToString(e));
                        logger.error("************   Redis Insert Error  **********    " + defectInfo.getOpeId() + " " + defectInfo.getGlassId() + " " + newValue);
                    }


                    //defect信息中的ope_id保留后4位
                    defectInfo.setOpeId(defectInfo.getOpeId().substring(1, defectInfo.getOpeId().length()));

                    //etl_timestamp赋值
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Timestamp nowTimestamp = Timestamp.valueOf(dateFormat.format(new Date()));
                    String nowTimestampStr = nowTimestamp.toString();
                    defectInfo.setEtlTimestamp(nowTimestampStr);

                    //shf_id,hour_id,cald_date等赋值
                    loadMESDataBean.setDefectHourSht(defectInfo);

                    //将defect_x_coord栏位超过8位的无效数据变更为8888888
                    if (null != defectInfo.getDefectXCoord() && defectInfo.getDefectXCoord().length() > 8) {
                        defectInfo.setDefectXCoord("8888888");
                        logger.info("glassId={} opeId={} jgeCnt={}数据存在问题，defect_x_coord为{}，defect_y_coord为{}。",
                                defectInfo.getGlassId(), defectInfo.getOpeId(), defectInfo.getJgeCnt(), defectInfo.getDefectXCoord(), defectInfo.getDefectYCoord());
                    }

                    //将defect_y_coord栏位超过8位的无效数据变更为8888888
                    if (null != defectInfo.getDefectYCoord() && defectInfo.getDefectYCoord().length() > 8) {
                        defectInfo.setDefectYCoord("8888888");
                        logger.info("glassId={} opeId={} jgeCnt={}数据存在问题，defect_x_coord为{}，defect_y_coord为{}。",
                                defectInfo.getGlassId(), defectInfo.getOpeId(), defectInfo.getJgeCnt(), defectInfo.getDefectXCoord(), defectInfo.getDefectYCoord());
                    }

                    //将Defect数据抛给Rabbit MQ
                    saveMesDefect(defectInfo);

                    logger.info("glassId={} opeId={} jgeCnt={}保存成功。",
                            defectInfo.getGlassId(), defectInfo.getOpeId(), defectInfo.getJgeCnt());

                    okCnt++;
                } catch (ETLException2 e) {
                    String err = e.getErrorCode().getErrorMsg() + "错误原因：" + e.getErrMsg();
                    logger.error("opeId={} glassId={} {}-{} {},错误原因：{}",
                            defectInfo.getOpeId(), defectInfo.getGlassId(), etlConfD.getRunStartTimestamp(), etlConfD.getRunEndTimestamp(), e.getErrorCode().getErrorMsg(), LoggerUtil.stackTraceToString(e));
                    if (StringUtils.isEmpty(err)) {
                        errMsg = "opeId=" + defectInfo.getOpeId() + " " + "glassId=" + defectInfo.getGlassId() + " " + err + "\n";
                    } else {
                        errMsg += "opeId=" + defectInfo.getOpeId() + " " + "glassId=" + defectInfo.getGlassId() + " " + err + "\n";

                    }
                    errCnt++;
                } catch (Exception e) {
                    String err = LoggerUtil.stackTraceToString(e);
                    logger.error("opeId={} glassId={} {}-{} 保存错误，错误原因：{}",
                            defectInfo.getOpeId(), defectInfo.getGlassId(), etlConfD.getRunStartTimestamp(), etlConfD.getRunEndTimestamp(), err);
                    if (StringUtils.isEmpty(err)) {
                        errMsg = "opeId=" + defectInfo.getOpeId() + " " + "glassId=" + defectInfo.getGlassId() + " " + err + "\n";
                    } else {
                        errMsg += "opeId=" + defectInfo.getOpeId() + " " + "glassId=" + defectInfo.getGlassId() + " " + err + "\n";

                    }
                    errCnt++;
                }
            }
        } else {
            logger.info("没有此区间的数据。"); // todo print log & save etl data
        }
        resMsg[0] = String.valueOf(okCnt);
        resMsg[1] = String.valueOf(errCnt);
        resMsg[2] = errMsg;

        return resMsg;
    }


    public void saveMesDefect(FileDefectPojo defectInfo) throws Exception {
        try {
            rabbitTemplate.convertAndSend("qmsExchange", "C_DEFECT", JacksonUtil.toJSONStr(defectInfo));
        } catch (Exception e) {
            logger.error(LoggerUtil.stackTraceToString(e));
            throw new ETLException2(ErrorCode.ERROR_RABBITMQ_MES_DEFECT, e);
        }
    }

    public Map<String, Integer> small2big(Integer panelNo, List<MppLayoutPnlD> layoutPnlDS, Double data,
                                         Double gate) throws CoordTransformException {
        Map<String, Integer> coord = new HashMap<>(16);
        if (!org.springframework.util.StringUtils.isEmpty(layoutPnlDS) && layoutPnlDS.size() > 0) {
            List<MppLayoutPnlD> panelLayout = layoutPnlDS.stream().filter(layout -> panelNo.equals(layout.getPanelNo())).collect(Collectors.toList());

            //若在表中找不到对应panel的数据，则返回横纵坐标为null
            MppLayoutPnlD mppLayoutPnlD = new MppLayoutPnlD();
            if (panelLayout != null && panelLayout.size() > 0) {
                mppLayoutPnlD = panelLayout.get(0);
            } else {
                coord.put("glassX", null);
                coord.put("glassY", null);
                return coord;
            }
            Double this_xSta = null;
            Double this_ySta = null;
            Double this_xLen = null;
            Double this_yLen = null;
            try {
                this_xSta = mppLayoutPnlD.getxCoord();
                this_ySta = mppLayoutPnlD.getyCoord();
                this_xLen = mppLayoutPnlD.getxAxisLength();
                this_yLen = mppLayoutPnlD.getyAxisLength();
            } catch (Exception e) {
                throw new CoordTransformException("坐标参数不全!");
            }
            Integer dataCnt = mppLayoutPnlD.getDataCnt();
            Integer gateCnt = mppLayoutPnlD.getGateCnt();
            if (dataCnt == null) {
                throw new CoordTransformException("该片未维护data!");
            }
            if (gateCnt == null) {
                throw new CoordTransformException("该片未维护gate!");
            }

            //计算该data gate在该片的x y
            double thisX = mppLayoutPnlD.getxCoord();
            double thisY = mppLayoutPnlD.getyCoord();
            double x2 = this_xSta + this_xLen;
            double y2 = this_ySta - this_yLen;
            double x = (thisX + (x2 - thisX) / dataCnt * data) * 1000;
            double y = (thisY - (thisY - y2) / gateCnt * gate) * 1000;
            BigDecimal b = new BigDecimal(x);
            BigDecimal b1 = new BigDecimal(y);
            Integer xx = b.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            Integer yy = b1.setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
            coord.put("glassX", xx);
            coord.put("glassY", yy);
        } else {
            logger.info("layoutPnl 为空");
        }
        return coord;
    }


    public HashMap<String, Integer> small2Big2(String panelId, List<MppLayoutPnlD> layoutPnlDS,
                                             Double data, Double gate) throws CoordTransformException {

        Double leftXStart = null;
        Double topYStart = null;
        Double xLength = null;
        Double yLength = null;
        Short dgOrigin = null;
        Short dgMapXy = null;
        Double rightXEnd = null;
        Double lowerYEnd = null;
        Integer dataCnt = null;
        Integer gateCnt = null;
        Double xx = null;
        Double yy = null;
        Integer x = null;
        Integer y = null;

        HashMap<String, Integer> returnCoord = new HashMap<>(16);

        Integer panelNo =  Integer.parseInt(panelId.substring(panelId.length() - 2, panelId.length()));
        List<MppLayoutPnlD> panelLayout = layoutPnlDS.stream().filter(layout -> panelNo.equals(layout.getPanelNo())).collect(Collectors.toList());

        //若在表中找不到对应panel的数据，则返回横纵坐标为null
        MppLayoutPnlD mppLayoutPnlD = new MppLayoutPnlD();
        if (panelLayout != null && panelLayout.size() > 0) {
            mppLayoutPnlD = panelLayout.get(0);
        } else {
            returnCoord.put("glassX", null);
            returnCoord.put("glassY", null);
            return returnCoord;
        }

        leftXStart = mppLayoutPnlD.getxCoord() * 1000;
        topYStart = mppLayoutPnlD.getyCoord() * 1000;
        xLength = mppLayoutPnlD.getxAxisLength() * 1000;
        yLength = mppLayoutPnlD.getyAxisLength() * 1000;
        dgOrigin = mppLayoutPnlD.getDgOrigin();
        dgMapXy = mppLayoutPnlD.getDgMapXy();
        rightXEnd = leftXStart + xLength;
        lowerYEnd = topYStart - yLength;
        dataCnt = mppLayoutPnlD.getDataCnt();
        gateCnt = mppLayoutPnlD.getGateCnt();

        if (0.0 == data && gate != 0.0) {
            data = Double.valueOf(dataCnt);
        }
        if (0.0 == gate && data != 0.0) {
            gate = Double.valueOf(gateCnt);
        }

        Double densityX = dgMapXy == DATA_TO_X ? data : gate;
        Double densityY = dgMapXy == DATA_TO_X ? gate : data;
        Integer dataCntX = dgMapXy == DATA_TO_X ? dataCnt : gateCnt;
        Integer gateCntY = dgMapXy == DATA_TO_X ? gateCnt : dataCnt;
        if (dgOrigin == TOP_LEFT || dgOrigin == LOWER_LEFT) {
            xx = (leftXStart + xLength / dataCntX * densityX);
        } else if (dgOrigin == TOP_RIGHT || dgOrigin == LOWER_RIGHT) {
            xx = (rightXEnd - xLength / dataCntX * densityX);
        }
        if (dgOrigin == TOP_LEFT || dgOrigin == TOP_RIGHT) {
            yy = (topYStart - yLength / gateCntY * densityY);
        } else if (dgOrigin == LOWER_RIGHT || dgOrigin == LOWER_LEFT) {
            yy = (lowerYEnd + yLength / gateCntY * densityY);
        }

        x = new BigDecimal(xx).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();
        y = new BigDecimal(yy).setScale(0, BigDecimal.ROUND_HALF_UP).intValue();

        returnCoord.put("glassX", x);
        returnCoord.put("glassY", y);
        return returnCoord;

    }

    public void updateRealFlg(String opeId, String glassId, String oldEvtTimestamp, String jgeCnt) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Timestamp nowTimestamp = Timestamp.valueOf(dateFormat.format(new Date()));

        String nowTimestampStr = nowTimestamp.toString();

        JSONObject update_columns = new JSONObject();
        update_columns.put("real_flg", "N");
        update_columns.put("etl_timestamp", nowTimestampStr);

        JSONObject query_conditions = new JSONObject();
        query_conditions.put("glass_id", glassId);
        query_conditions.put("ope_id", opeId);
        query_conditions.put("jge_cnt", jgeCnt);
        query_conditions.put("real_flg", "Y");
        query_conditions.put("evt_timestamp", oldEvtTimestamp);

        JSONObject updateJsonObj = new JSONObject();
        updateJsonObj.put("update_flg", "true");
        updateJsonObj.put("ope_tbl_name", "wpp_cdefect_f_n");
        updateJsonObj.put("query_conditions", query_conditions);
        updateJsonObj.put("update_columns", update_columns);

        try {
            rabbitTemplate.convertAndSend("qmsExchange", "C_DEFECT", JacksonUtil.toJSONStr(updateJsonObj));
        } catch (Exception e) {
            logger.error(LoggerUtil.stackTraceToString(e));
            throw new ETLException2(ErrorCode.ERROR_RABBITMQ_GP_REALFLG, e);
        }
    }
}
